package cn.demoncat.util.web;

import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.exec.ExecUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StatusConstant.Common;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.constant.SystemConstant;
import cn.demoncat.util.lang.entity.TypeList;
import cn.demoncat.util.web.constant.IpConstant;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;

/**
 * IP 处理工具
 * 
 * @author 延晓磊
 *
 * @since 2017年9月13日
 */
public class IpUtil {

	/**
	 * 本机IP
	 */
	public static final InetAddress LOCAL_ADDRESS = getLocalInetAddress();
	/**
	 * 本机IP
	 */
	public static final String LOCAL_IP = LOCAL_ADDRESS != null ? LOCAL_ADDRESS.getHostAddress() : IpConstant.LOCALHOST;
	/**
     * 本地IP，纯数字
     */
    public static final String LOCAL_IP_NUM = parseIpToNum(LOCAL_IP);
    
    /**
	 * IP相关的 proxy header
	 */
	private static final String[] IP_HEADERS = {
			// 不同的HTTP服务器/反向代理服务器的IP标识头
			"X-Forwarded-For","X-Real-IP","Proxy-Client-IP","WL-Proxy-Client-IP","HTTP_CLIENT_IP","HTTP_X_FORWARDED_FOR"
	};

	/**
	 * 判断是否为IP地址
	 * 
	 * @param ip
	 * @return
	 */
	public static boolean isIpAddr(String ip) {
		// 修剪
		ip = StringUtils.trimToNull(ip);
		// 校验
		return ip != null && IpConstant.IP_PATTERN.matcher(ip).matches();
	}
	
	/**
	 * 判断是否为IP地址
	 * 
	 * @param address
	 * @return
	 */
	public static boolean isIpAddr(InetAddress address) {
		if (address == null) {
			return false;
		}
		return isIpAddr(address.getHostAddress());
	}
	
	/**
	 * 判断是否为本地IP
	 * 
	 * @param ip	127.0.0.1
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年11月20日
	 */
	public static boolean isLocalIp(String ip) {
		return IpConstant.LOCALHOST.equals(ip);
	}
	
	/**
	 * 判断是否为内网
	 * 
	 * @param ip	10|172|192
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年11月20日
	 */
	public static boolean isIntranetIp(String ip) {
		return IpConstant.INTRANET_PATTERN.matcher(ip).matches();
	}
	
	/**
	 * 判断是否为公网
	 * 
	 * @param ip
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年6月21日
	 */
	public static boolean isPublicIp(String ip) {
		return !isLocalIp(ip) && !isIntranetIp(ip) && !IpConstant.ANYHOST.equals(ip);
	}

	/**
	 * 获取请求者的客户端IP地址
	 *
	 * @return IP
	 */
	public static String getRequesterIp() {
		return getRequesterIp(ServletUtil.getRequest());
	}

	/**
	 * 获取请求者的客户端IP地址
	 * 
	 * @param request
	 * 
	 * @return IP
	 */
	public static String getRequesterIp(HttpServletRequest request) {
		// 根据各种反向代理的设置，获取客户端的IP
		String ip = null;
		boolean flag = false;
		for (String ipHeader : IP_HEADERS) {
			ip = request.getHeader(ipHeader);
			if (isIpAddr(ip)) {
				flag = true;
				break;
			}
		}
		if (!flag) {
			ip = request.getRemoteAddr();
		}
		// 修剪
		ip = StringUtils.trimToEmpty(ip);
		// 由于客户端或Nginx配置问题，有时会获取到多个ip
		ip = StringUtil.splitStrict(ip)[0];
		return ip.equals(IpConstant.UNKNOWN) ? IpConstant.LOCALHOST : ip;
	}

	/**
	 * 转换IP，去除.，变成纯数字
	 * 
	 * @param ip
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年8月30日
	 */
	public static String parseIpToNum(String ip) {
		return ip.replaceAll("\\.", StringConstant.EMPTY);
	}
	
	/**
	 * 转换IP，将.改为-
	 * 
	 * @param ip
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年8月30日
	 */
	public static String parseIpToBar(String ip) {
		return ip.replaceAll("\\.", StringConstant.BAR);
	}
	
	/**
	 * 转换IP，将.改为x
	 * 
	 * @param ip
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年8月30日
	 */
	public static String parseIp(String ip,String x) {
		return ip.replaceAll("\\.", x);
	}
	
	/**
	 * 获取本地IP 地址(通常返回内网IP)
	 * 
	 * 注：服务器可能有多个IP(127.0.0.1，内网IP，外网IP，多网卡IP)
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年9月13日
	 */
	private static InetAddress getLocalInetAddress() {
		try {
			//获取本地IP
			InetAddress localAddress = InetAddress.getLocalHost();
			if (isIpAddr(localAddress)) {
				return localAddress;
			}
			// 如果未找到，通过网卡获取
			Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
			if (interfaces != null) {
				NetworkInterface network;
				Enumeration<InetAddress> addresses;
				InetAddress address;
				while (interfaces.hasMoreElements()) {
					network = interfaces.nextElement();
					addresses = network.getInetAddresses();
					while (addresses.hasMoreElements()) {
						address = addresses.nextElement();
						if (isIpAddr(address)) {
							return address;
						}
					}
				}
			}
		} catch (Exception e) {
			// 获取失败
			return null;
		}
		return null;
	}
	
	/**
	 * 获取InetAddress
	 * 
	 * @param host	域名/IP
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月4日
	 */
	public static InetAddress getAddr(String host) {
		try {
			return InetAddress.getByName(host);
		} catch (UnknownHostException e) {
			throw new ParamRuntimeException("解析网络地址失败", e);
		}
	}
	
	/**
	 * 获取 InetSocketAddress
	 * 
	 * @param host	域名/IP
	 * @param port	端口
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月29日
	 */
	public static InetSocketAddress getSocketAddr(String host, int port) {
		return new InetSocketAddress(getAddr(host), port);
	}
	
	/**
	 * 获取 InetSocketAddress
	 * 
	 * @param url	IP:PORT
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月29日
	 */
	public static InetSocketAddress getSocketAddr(String url) {
		String[] addr = StringUtils.split(url,StringConstant.COLON);
		return getSocketAddr(addr[0], Integer.parseInt(addr[1]));
	}
	
	
	/**
	 * IP转Long
	 * 
	 * @param ip
	 * @return long
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年5月7日
	 */
    public static long toLong( String ip ) {
    	// 截取
        String[] p = StringUtils.split(ip, '.');
        // 非IP
        if ( p.length != 4 ) return 0;
        // 转换为Long
        int p1 = ((Integer.parseInt(p[0]) << 24) & 0xFF000000);
        int p2 = ((Integer.parseInt(p[1]) << 16) & 0x00FF0000);
        int p3 = ((Integer.parseInt(p[2]) <<  8) & 0x0000FF00);
        int p4 = ((Integer.parseInt(p[3])) & 0x000000FF);
        return ((p1 | p2 | p3 | p4) & 0xFFFFFFFFL);
    }

    /**
     * Long转换IP
     * 
     * @param ip
     * @return
     * 
     * @author 延晓磊
     *
     * @since 2020年5月7日
     */
    public static String toIp(long ip) {
    	return ((ip >> 24) & 0xFF) + StringConstant.POINT +
				((ip >> 16) & 0xFF) + StringConstant.POINT +
				((ip >> 8) & 0xFF) + StringConstant.POINT +
				((ip) & 0xFF);
    }
    
    /**
     * 严格校验IPV4
     * 
     * @param ip
     * @return
     * 
     * @author 延晓磊
     *
     * @since 2020年5月7日
     */
    public static boolean isIp(String ip){
    	// 截取
        String[] p = StringUtils.split(ip, '.');
        // IPV4
        if ( p.length != 4 ) return false;
        // 0-255
        for (String pp : p ) {
            if ( pp.length() > 3 ) return false;
            int val = Integer.parseInt(pp);
            if ( val > 255 ) return false;
        }
        return true;
    }
    
    /**
     * PING
     * 
     * @param ip
     * @return
     * 
     * @author 延晓磊
     *
     * @since 2020-9-10
     */
    public static boolean ping(String ip) {
    	// OS类型
    	String params;
    	if (SystemConstant.OS_IS_LINUX) {
    		// 执行1次，等待1秒
    		params = "ping -c 1 -w  1 " + ip;
		}else {
			// 执行1次，等待100毫秒
			params = "ping " + ip + " -n 1 -w 100";
		}
    	// 执行命令
    	TypeList<String> exec = ExecUtil.exec(params);
    	// 判断结果：响应中包含TTL或ttl
    	if (Common.isYes(exec.getType())) {
			for (String line : exec.getList()) {
				if (line.contains("ttl") || line.contains("TTL")) {
					return true;
				}
			}
		}
    	return false;
    }
	
}
